//
// Created by ta&to on 2022/8/25.
//

#include "http_string.h"

uint64_t http_numlen(uint64_t num)
{
    uint64_t len = 0;        // 初始长度为0
    for(; num > 0; ++len)    // 判断num是否大于0，否则长度+1
        num /= 10;	         // 使用除法进行运算，直到num小于1
    return len;              // 返回长度的值
}

char *http_sprintf(char *buf, const char *fmt, ...)
{
    char   *p;
    va_list   args;

    va_start(args, fmt);
    p = http_vslprintf(buf, (void *) -1, fmt, args);
    va_end(args);

    return p;
}

char *http_snprintf(char *buf, size_t max, const char *fmt, ...)
{
    char   *p;
    va_list   args;

    va_start(args, fmt);
    p = http_vslprintf(buf, buf + max, fmt, args);
    va_end(args);

    return p;
}
char *http_slprintf(char *buf, char *last, const char *fmt, ...)
{
    char   *p;
    va_list   args;

    va_start(args, fmt);
    p = http_vslprintf(buf, last, fmt, args);
    va_end(args);

    return p;
}
char *http_vslprintf(char *buf, char *last, const char *fmt, va_list args)
{
    char                *p, zero;
    int                    d;
    double                 f;
    size_t                 slen;
    int64_t                i64;
    int64_t               ui64, frac;
    char                   *ms;
    int                  width, sign, hex, max_width, frac_width, scale, n;
    http_str_t              *v;

    while (*fmt && buf < last) {

        /*
         * "buf < last" means that we could copy at least one character:
         * the plain character, "%%", "%c", and minus without the checking
         */

        if (*fmt == '%') {

            i64 = 0;
            ui64 = 0;

            zero = (char) ((*++fmt == '0') ? '0' : ' ');
            width = 0;
            sign = 1;
            hex = 0;
            max_width = 0;
            frac_width = 0;
            slen = (size_t) -1;

            while (*fmt >= '0' && *fmt <= '9') {
                width = width * 10 + (*fmt++ - '0');
            }


            for ( ;; ) {
                switch (*fmt) {

                    case 'u':
                        sign = 0;
                        fmt++;
                        continue;

                    case 'm':
                        max_width = 1;
                        fmt++;
                        continue;

                    case 'X':
                        hex = 2;
                        sign = 0;
                        fmt++;
                        continue;

                    case 'x':
                        hex = 1;
                        sign = 0;
                        fmt++;
                        continue;

                    case '.':
                        fmt++;

                        while (*fmt >= '0' && *fmt <= '9') {
                            frac_width = frac_width * 10 + (*fmt++ - '0');
                        }

                        break;

                    case '*':
                        slen = va_arg(args, size_t);
                        fmt++;
                        continue;

                    default:
                        break;
                }

                break;
            }


            switch (*fmt) {

                case 'V':
                    v = va_arg(args, http_str_t *);

                    buf = http_sprintf_str(buf, last, v->data, v->len, hex);
                    fmt++;

                    continue;

                case 's':
                    p = va_arg(args, char *);

                    buf = http_sprintf_str(buf, last, p, slen, hex);
                    fmt++;

                    continue;

                case 'O':
                    i64 = (int64_t) va_arg(args, off_t);
                    sign = 1;
                    break;

                case 'P':
                    i64 = (int64_t) va_arg(args, pid_t);
                    sign = 1;
                    break;

                case 'T':
                    i64 = (int64_t) va_arg(args, time_t);
                    sign = 1;
                    break;

                case 'z':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, size_t);
                    } else {
                        ui64 = (int64_t) va_arg(args, size_t);
                    }
                    break;

                case 'i':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, int);
                    } else {
                        ui64 = (int64_t) va_arg(args, int);
                    }

                    if (max_width) {
                        width = INT_T_LEN;
                    }

                    break;

                case 'd':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, int);
                    } else {
                        ui64 = (int64_t) va_arg(args, uint32_t);
                    }
                    break;

                case 'l':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, long);
                    } else {
                        ui64 = (int64_t) va_arg(args, unsigned long);
                    }
                    break;

                case 'D':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, int32_t);
                    } else {
                        ui64 = (int64_t) va_arg(args, uint32_t);
                    }
                    break;

                case 'L':
                    if (sign) {
                        i64 = va_arg(args, int64_t);
                    } else {
                        ui64 = va_arg(args, int64_t);
                    }
                    break;

                case 'A':
                    if (sign) {
                        i64 = (int64_t) va_arg(args, int32_t);
                    } else {
                        ui64 = (int64_t) va_arg(args, uint32_t);
                    }

                    if (max_width) {
                        width = ATOMIC_T_LEN;
                    }

                    break;

                case 'f':
                    f = va_arg(args, double);

                    if (f < 0) {
                        *buf++ = '-';
                        f = -f;
                    }

                    ui64 = (int64_t) f;
                    frac = 0;

                    if (frac_width) {

                        scale = 1;
                        for (n = frac_width; n; n--) {
                            scale *= 10;
                        }

                        frac = (int64_t) ((f - (double) ui64) * scale + 0.5);

                        if (frac == scale) {
                            ui64++;
                            frac = 0;
                        }
                    }

                    buf = http_sprintf_num(buf, last, ui64, zero, 0, width);

                    if (frac_width) {
                        if (buf < last) {
                            *buf++ = '.';
                        }

                        buf = http_sprintf_num(buf, last, frac, '0', 0, frac_width);
                    }

                    fmt++;

                    continue;

                case 'p':
                    ui64 = (uintptr_t) va_arg(args, void *);
                    hex = 2;
                    sign = 0;
                    zero = '0';
                    width = 2 * sizeof(void *);
                    break;

                case 'c':
                    d = va_arg(args, int);
                    *buf++ = (char) (d & 0xff);
                    fmt++;

                    continue;

                case 'Z':
                    *buf++ = '\0';
                    fmt++;

                    continue;

                case 'N':

                    *buf++ = LF;
                    fmt++;

                    continue;

                case '%':
                    *buf++ = '%';
                    fmt++;

                    continue;

                default:
                    *buf++ = *fmt++;

                    continue;
            }

            if (sign) {
                if (i64 < 0) {
                    *buf++ = '-';
                    ui64 = (int64_t) -i64;

                } else {
                    ui64 = (int64_t) i64;
                }
            }

            buf = http_sprintf_num(buf, last, ui64, zero, hex, width);

            fmt++;

        } else {
            *buf++ = *fmt++;
        }
    }

    return buf;
}

static char *http_sprintf_num(char *buf, char *last, int64_t ui64, char zero,
            int hexadecimal, int width)
{
    char         *p, temp[INT64_LEN + 1];
    /*
     * we need temp[INT64_LEN] only,
     * but icc issues the warning
     */
    size_t          len;
    uint32_t        ui32;
    static char   hex[] = "0123456789abcdef";
    static char   HEX[] = "0123456789ABCDEF";

    p = temp + INT64_LEN;

    if (hexadecimal == 0) {

        if (ui64 <= (int64_t) MAX_UINT32_VALUE) {

            /*
             * To divide 64-bit numbers and to find remainders
             * on the x86 platform gcc and icc call the libc functions
             * [u]divdi3() and [u]moddi3(), they call another function
             * in its turn.  On FreeBSD it is the qdivrem() function,
             * its source code is about 170 lines of the code.
             * The glibc counterpart is about 150 lines of the code.
             *
             * For 32-bit numbers and some divisors gcc and icc use
             * a inlined multiplication and shifts.  For example,
             * unsigned "i32 / 10" is compiled to
             *
             *     (i32 * 0xCCCCCCCD) >> 35
             */

            ui32 = (uint32_t) ui64;

            do {
                *--p = (char) (ui32 % 10 + '0');
            } while (ui32 /= 10);

        } else {
            do {
                *--p = (char) (ui64 % 10 + '0');
            } while (ui64 /= 10);
        }

    } else if (hexadecimal == 1) {

        do {

            /* the "(uint32_t)" cast disables the BCC's warning */
            *--p = hex[(uint32_t) (ui64 & 0xf)];

        } while (ui64 >>= 4);

    } else { /* hexadecimal == 2 */

        do {

            /* the "(uint32_t)" cast disables the BCC's warning */
            *--p = HEX[(uint32_t) (ui64 & 0xf)];

        } while (ui64 >>= 4);
    }

    /* zero or space padding */

    len = (temp + INT64_LEN) - p;

    while (len++ < width && buf < last) {
        *buf++ = zero;
    }

    /* number safe copy */

    len = (temp + INT64_LEN) - p;

    if (buf + len > last) {
        len = last - buf;
    }

    return http_cpymem(buf, p, len);
}

static char *http_sprintf_str(char *buf, char *last, char *src, size_t len,
            int hexadecimal)
{
    static char   hex[] = "0123456789abcdef";
    static char   HEX[] = "0123456789ABCDEF";

    if (hexadecimal == 0) {

        if (len == (size_t) -1) {
            while (*src && buf < last) {
                *buf++ = *src++;
            }

        } else {
            len = log_min((size_t) (last - buf), len);
            buf = http_cpymem(buf, src, len);
        }

    } else if (hexadecimal == 1) {

        if (len == (size_t) -1) {

            while (*src && buf < last - 1) {
                *buf++ = hex[*src >> 4];
                *buf++ = hex[*src++ & 0xf];
            }

        } else {

            while (len-- && buf < last - 1) {
                *buf++ = hex[*src >> 4];
                *buf++ = hex[*src++ & 0xf];
            }
        }

    } else { /* hexadecimal == 2 */

        if (len == (size_t) -1) {

            while (*src && buf < last - 1) {
                *buf++ = HEX[*src >> 4];
                *buf++ = HEX[*src++ & 0xf];
            }

        } else {

            while (len-- && buf < last - 1) {
                *buf++ = HEX[*src >> 4];
                *buf++ = HEX[*src++ & 0xf];
            }
        }
    }

    return buf;
}

void http_amemcpy(char *dst, char *src)
{
    memset(dst, 0, strlen(dst));
    (void) memcpy(dst, src, strlen(src));
}